package com.mocha.nezha.nezha;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.mocha.nezha.dmo.*;
import com.mocha.nezha.dmo.entity.VehicleTrackRecordPGEntity;
import com.mocha.nezha.repository.VehicleTrackRecordPGRepository;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author songkui
 * @since 2024/3/21 15:25
 */
@Component
@Slf4j
@AllArgsConstructor
public class VehicleTrackHandler {

    private final VehicleTrackRecordPGRepository vehicleTrackRecordPGRepository;

    public void doTrack(String park, VehicleTrackDTO trackDTO){
        log.info("handler>>  park:{},  {}", park, trackDTO);
        //1, 通过next 找下一个杆子  下一个杆子可能为空
        PoleVertex newPole = NezhaDeviceManager.getInstance().getPoleVertexByCameraCode(park, trackDTO.getCno());
        if (null == newPole){
            log.error("handler>>  nezha pole is null, [park: {}, objId {},  cno: {}]", park,  trackDTO.getOid(), trackDTO.getCno());
            return;
        }

        //2 前端是否有摄像头
        String cameraCode = trackDTO.getCno();
        Set<NezhaClient> clientSet = NezhaSessionManager.getInstance().listClientByCameraCode(park, cameraCode);
        if (CollectionUtil.isEmpty(clientSet)){
            log.info("handler>> client config camera is null, [park: {}, objId {},  cameraCode: {}]", park, trackDTO.getOid(), trackDTO.getCno());
            saveRecord(park, trackDTO, newPole.getPoleCode());
            return;
        }


        VehicleBO vehicleBO = VehicleManager.getInstance().getVehicleByDTO(park, trackDTO); // vehicle
        log.info("handler>>  objectId: {}, nezha poleCoe: {},   new next camera: {}",
                trackDTO.getOid(), Optional.ofNullable(vehicleBO.getCurrentPole()).map(PoleVertex::getPoleCode).orElse("null"),  String.valueOf(trackDTO.getNco()));
        PoleVertex nextPole = NezhaDeviceManager.getInstance().getPoleVertexByCameraCode(park, trackDTO.getNco());
        if (StrUtil.isNotEmpty(trackDTO.getNco()) && null == nextPole){
            log.error("handler>> dto npl not null but nezha poleVertex is null [park: {}, objectId: {}, next camera code:{}]", park, trackDTO.getOid(), trackDTO.getNco());
        }

        if (newPole.equals(nextPole)){
            nextPole = null;
            log.info("handler>> now pole and next pole is same [park: {}, objectId: {}, now CM code:{}, next CM code:{}, pole: {}]", park, trackDTO.getOid(), trackDTO.getCno(), trackDTO.getNco(), newPole.getPoleCode());
        }


        VehiclePathMessage message = null == nextPole ? doNotNextPole(park, vehicleBO, newPole) : doNextPole(park, vehicleBO, newPole, nextPole);
        //send msg
        if (null != message){
            for (NezhaClient client : clientSet){
                client.sendMessage(message);
            }
        }
        //saveRecode
        saveRecord(park, trackDTO, newPole.getPoleCode());

    }


    private VehiclePathMessage doNotNextPole(String parkCode, VehicleBO vehicleBO, PoleVertex newPole){
        //1, 判断 汽车原来有没有节点
        if (null == vehicleBO.getCurrentPole()){
            log.info("handler>> Not next pole-- Vehicle current pole is null , next is nul, Uncertain direction [park: {}, objectId: {}] ", vehicleBO.getParkCode(), vehicleBO.getObjectId());
            vehicleBO.setCurrentPole(newPole);
            return null;
        }
        //和本节点相同则等待
        if (vehicleBO.getCurrentPole().equals(newPole)){
            log.info("handler>> Not next pole-- Vehicle current pole equals new Pole [park: {}, objectId: {}, current pole: {},  new pole: {}] ",
                    vehicleBO.getParkCode(), vehicleBO.getObjectId(), vehicleBO.getCurrentPole().getPoleCode(), newPole.getPoleCode());
            return null;
        }

        //不一致
        log.info("handler>> Not next pole-- current pole and next pole different  [park: {}, objectId: {},  current pole: {},  new pole: {}] ",
                     vehicleBO.getParkCode(), vehicleBO.getObjectId(), vehicleBO.getCurrentPole().getPoleCode(), newPole.getPoleCode());

        List<PointMessage> pointList = betweenPole(parkCode, vehicleBO, vehicleBO.getCurrentPole(), newPole);
        if (CollectionUtil.isEmpty(pointList)){
            log.info("handler>> Not next pole-- compute point is null  [park: {}, objectId: {},  current pole: {},  new pole: {}] ",
                    vehicleBO.getParkCode(), vehicleBO.getObjectId(), vehicleBO.getCurrentPole().getPoleCode(), newPole.getPoleCode());
            vehicleBO.setCurrentPole(newPole);
            return null;
        }

        return buildVehiclePathMessage(vehicleBO, pointList, newPole);

    }

    //有下一个节点
    private VehiclePathMessage doNextPole(String parkCode, VehicleBO vehicleBO, PoleVertex newPole, PoleVertex nextPole){

        List<PointMessage> pointList;
        if (nextPole.equals(vehicleBO.getCurrentPole())){
            log.info("handler>>  Next pole and Vehicle current pole is same  [park: {}, objectId: {}, current pole: {},  newPole: {}, nextPole: {}] ",
                    vehicleBO.getParkCode(), vehicleBO.getObjectId(), vehicleBO.getCurrentPole().getPoleCode(), newPole.getPoleCode(), nextPole.getPoleCode());
            return null;
        }

        //1, 汽车原来有没有节点
        if (null == vehicleBO.getCurrentPole() || vehicleBO.getCurrentPole().equals(newPole)){ //一致
            log.info("handler>>  Next pole-- new Vehicle or same  [park: {}, objectId: {},  newPole: {}, nextPole: {}] ",
                    vehicleBO.getParkCode(), vehicleBO.getObjectId(), newPole.getPoleCode(), nextPole.getPoleCode());
            pointList = betweenPole(parkCode, vehicleBO, newPole, nextPole);

        }else {
            pointList = Optional.ofNullable(betweenPole(parkCode, vehicleBO, vehicleBO.getCurrentPole(), newPole)).orElse(new ArrayList<>()); //当前节点 和新节点 轨迹
            List<PointMessage> secendList = betweenPole(parkCode, vehicleBO, newPole, nextPole);
            if (CollectionUtil.isNotEmpty(secendList)){
                pointList.addAll(secendList);
            }
        }

        if (CollectionUtil.isEmpty(pointList)){
            log.info("handler>> Next pole-- compute point is null  [park: {}, objectId: {} current pole: {},  new pole: {}] ",
                    vehicleBO.getParkCode(), vehicleBO.getObjectId(), vehicleBO.getCurrentPole().getPoleCode(), newPole.getPoleCode());
            vehicleBO.setCurrentPole(newPole);
            return null;
        }

        return buildVehiclePathMessage(vehicleBO, pointList, nextPole);

    }


    //两个节点之间 定位数据
    private List<PointMessage> betweenPole(String parkCode, VehicleBO vehicleBO, PoleVertex startPole, PoleVertex endPole){
        log.info("handler>> between vertex [from: {} ----> to: {} | parkCode {}, objectId: {}]", startPole.getPoleCode(), endPole.getPoleCode(), parkCode, vehicleBO.getObjectId());
        //不一致
        List<PoleVertex> vertexList = NezhaDeviceManager.getInstance().findVertexPaths(parkCode, startPole, endPole);
        if (CollectionUtil.isEmpty(vertexList) || vertexList.size() == 1 || vertexList.size() > 50){
            log.info("handler>>  between vertex-- vertex list is null or size > 5  [park: {}, objectId: {}, current pole: {},  new pole: {}, vertexList size: {}] ",
                    vehicleBO.getParkCode(), vehicleBO.getObjectId(), null != vehicleBO.getCurrentPole() ? vehicleBO.getCurrentPole().getPoleCode(): "is null", endPole.getPoleCode(), CollectionUtil.isEmpty(vertexList) ? "0" : vertexList.size());
            return null;
        }

        List<PoleEdge> edgeList = NezhaDeviceManager.getInstance().findEdgePaths(parkCode, startPole, endPole);
        if (CollectionUtil.isEmpty(edgeList) || edgeList.size() > 50){
            log.info("handler>>  between vertex-- edge list is null or size > 5  [park: {}, objectId: {}, current pole: {},  new pole: {}, edgeList size: {}] ",
                    vehicleBO.getParkCode(), vehicleBO.getObjectId(), vehicleBO.getCurrentPole().getPoleCode(), endPole.getPoleCode(), CollectionUtil.isEmpty(edgeList) ? "0" : edgeList.size());
            return null;
        }

        return computePoint(vertexList, edgeList, vehicleBO);
    }



    private List<PointMessage> computePoint(List<PoleVertex> vertexList, List<PoleEdge> edgeList, VehicleBO vehicleBO){
        log.info("handler>> compute point, [objectId {},  vertex: {}]", vehicleBO.getObjectId(), vertexList.stream().map(PoleVertex::getPoleCode).collect(Collectors.joining(",")) );
        log.info("handler>> compute point, [objectId {},  edge: {}]", vehicleBO.getObjectId(), edgeList.stream().map(PoleEdge::toString).collect(Collectors.joining(",")) );

        List<PointMessage> pointList = new ArrayList<>();

        int j = 0;
        for (int i = 0; i < vertexList.size() - 1; i++) {
            PoleVertex from = vertexList.get(i);
            PoleVertex to = vertexList.get(i + 1);
            PoleEdge edge = edgeList.get(j);
            List<PointBO> pointBOList = null;
            if (edge.findSource().equals(from)){
                  pointBOList = edge.getPointList();
            }else { // 反序
                List<PointBO> pointBOListTemp = edge.getPointList();
                if (CollectionUtil.isNotEmpty(pointBOListTemp)){
                    pointBOList = new ArrayList<>(pointBOListTemp);
                    Collections.reverse(pointBOList);
                }
            }

            if (CollectionUtil.isEmpty(pointBOList)){
                pointBOList = new ArrayList<>();
                pointBOList.add(new PointBO(from.getLon(), from.getLat(), from.getHeight(), 0, 0d));
                pointBOList.add(new PointBO(to.getLon(), to.getLat(), to.getHeight(), edge.getDistance(), 60d));
            }

            for (PointBO bo : pointBOList){
                pointList.add(bo.buildMessage(vehicleBO.buildTime(bo.getT())));
            }

            j++;
        }

        return pointList;
    }



    private VehiclePathMessage buildVehiclePathMessage(VehicleBO vehicleBO, List<PointMessage> pointMessages, PoleVertex newPole){
        vehicleBO.setCurrentPole(newPole);
        VehiclePathMessage message = new VehiclePathMessage();
        message.setVehicleId(vehicleBO.getObjectId());
        message.setVehicleNo(vehicleBO.getVehicleNo());
        message.setPoints(pointMessages);
        return message;
    }


    private void saveRecord(String park, VehicleTrackDTO trackDTO, String poleCode){
        VehicleTrackRecordPGEntity entity = new VehicleTrackRecordPGEntity();
        entity.setParkCode(park);
        entity.setPoleCode(poleCode);
        entity.setCameraCode(trackDTO.getCno());
        entity.setObjectId(trackDTO.getOid());

        entity.setVehicleNo(trackDTO.getVno());
        entity.setVelocity(trackDTO.getV());
        entity.setLon(trackDTO.getLon());
        entity.setLat(trackDTO.getLat());
        entity.setVehicleType(trackDTO.getType());
        entity.setNextPole(trackDTO.getNco());
        entity.setAddTime(LocalDateTime.now());
        vehicleTrackRecordPGRepository.save(entity);
    }



}
